{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 懒初始化策略综述"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "多线程环境下的懒初始化核心目标：**让昂贵或依赖外部状态的初始化逻辑只执行一次，并且保证线程安全**。常见方案可分为以下几类：\n",
    "\n",
    "| 策略名称                              | 关键语法 / 组件                   | 初始化后访问是否无锁 | 实现复杂度 | 典型场景                     |\n",
    "| ------------------------------------- | --------------------------------- | -------------------- | ---------- | ---------------------------- |\n",
    "| 函数内静态局部变量                    | `static T obj{...};`              | 是                   | ★☆☆        | 简单单例、配置/日志、注册表  |\n",
    "| `std::call_once` + `std::once_flag`   | `std::call_once(flag, init);`     | 是                   | ★★☆        | 跨函数重复使用一次性逻辑     |\n",
    "| 双重检查锁定 (DCLP)                   | `atomic + mutex + CAS`            | 是                   | ★★★        | 高频读、极致性能需求         |\n",
    "| 互斥锁包装                            | `std::mutex`                      | 否（初始化后仍需锁） | ★☆☆        | 初始化耗时长但访问不频繁     |\n",
    "| 自旋锁 (`std::atomic_flag`)           | 自定义 `SpinLock`                 | 否（持锁期间忙等）   | ★★☆        | 初始化极短、线程数少         |\n",
    "| 原子 `std::shared_ptr`                | `std::atomic<std::shared_ptr<T>>` | 是                   | ★★☆        | 高频读、共享所有权管理       |\n",
    "| 其它：`std::shared_future` / 依赖注入 | `std::promise` / 配置装配         | 取决于实现           | 视情况     | 异步初始化、可测试性优先场景 |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**优先级建议**\n",
    "\n",
    "1. 简单单例：函数内静态局部变量。\n",
    "2. 需要跨作用域共享一次性逻辑：`std::call_once`。\n",
    "3. 性能敏感且能正确处理内存序：双重检查锁定或原子 `shared_ptr`。\n",
    "4. 初始化耗时长但调用不频繁：互斥锁包装；初始化极快时可考虑自旋锁。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 各策略详解"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.1 函数内静态局部变量\n",
    "\n",
    "- **基本思路**：在函数内部声明 `static` 对象，第一次进入函数时构造，后续直接复用。\n",
    "- **优势**：语法最简洁、由 C++11 标准保证线程安全；初始化后访问零开销。\n",
    "- **注意事项**：\n",
    "  - 构造异常会在下一次调用时重试。\n",
    "  - 对象析构顺序由编译器决定（程序退出阶段），若不想自动析构可改用 `new` + `std::unique_ptr`.\n",
    "  - 单元测试中可能跨用例保留状态，需要手动重置。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.2 `std::call_once`\n",
    "\n",
    "- **基本思路**：配合 `std::once_flag` 确保给定初始化函数最多执行一次。\n",
    "- **优势**：显式控制，多个函数可共享同一个 `once_flag`；异常会在后续重试。\n",
    "- **注意事项**：`once_flag` 不能复制/重置；初始化耗时会阻塞所有等待线程。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.3 双重检查锁定 (DCLP)\n",
    "\n",
    "- **基本思路**：先无锁读取原子指针，若为空再加锁并复查；仅真正需要初始化时进入临界区。\n",
    "- **优势**：初始化后读路径完全无锁，性能优异。\n",
    "- **注意事项**：实现中必须使用原子操作及正确内存序；维护成本高，稍有疏忽即出现半初始化可见性问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.4 互斥锁包装\n",
    "\n",
    "- **基本思路**：使用 `std::mutex` 保护初始化逻辑；每次获取实例都加锁。\n",
    "- **优势**：实现最容易、语义清晰、易于调试。\n",
    "- **注意事项**：即使初始化完成后，访问仍需取得锁；在高频访问场景下可能成为瓶颈。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.5 自旋锁 (`std::atomic_flag`)\n",
    "\n",
    "- **基本思路**：用忙等方式实现轻量锁，适合极短临界区。\n",
    "- **优势**：避免系统调用，锁竞争开销小。\n",
    "- **注意事项**：初始化耗时稍长就会浪费 CPU；线程众多时更易造成抢占问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.6 原子 `std::shared_ptr`\n",
    "\n",
    "- **基本思路**：用 `std::atomic<std::shared_ptr<T>>` 存储实例；首次调用分配对象并用 `compare_exchange` 发布。\n",
    "- **优势**：读路径无锁且具备引用计数管理，适合共享所有权。\n",
    "- **注意事项**：首次竞争存在 CAS 循环；需要考虑引用循环或析构时机。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.7 其他思路\n",
    "\n",
    "- **`std::shared_future` / `std::promise`**：首次调用触发异步任务，适合耗时 I/O 或可并行初始化。\n",
    "- **依赖注入 / 配置化**：在应用启动或测试阶段显式提供实例，避免全局状态，更易测试。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "C++17",
   "language": "C++17",
   "name": "xcpp17"
  },
  "language_info": {
   "name": "C++17"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
